import java.net.DatagramPacket;
import java.net.InetAddress;

/**
 *
 *@author Paul Ozog
 *
 * Implements a the chat protocol.  Defines how packets are created
 * in accordance with spec.  The code here doesn't define an "object";
 * more of a collection of functions and constants that the server and
 * client use.
 * 
 * The Basic structure of the Data field of the UDP packet is:
 * [keywordID message...] (256 bytes total)
 *
 * We assume that the server only calls createIncommingPacket and the client
 * may call either createMessagePacket or createGreetingPacket. 
 * The extractMessage and extractKeywordID will be called by either the
 * client or server.
 */
class ChatProtocol {
    public static int  PACKETSIZE     = 256;
    public static int  KEYWORDLENGTH  = 1;
    public static int  MESSAGELENGTH  = PACKETSIZE - KEYWORDLENGTH; //255
    public static byte GREETING  = 1;
    public static byte MESSAGE   = 2;
    public static byte INCOMMING = 3;

    /**
     * @param message - String (ie - "Hello world!")
     * @param origAddress - InetAddress of the client who sent the
     *                      MESSAGE.
     * @param origPort - int port of the client who sent the 
     *                   Message
     * @param destAddress - InetAddress of the packets destination
     * @param destPort    - int port # of the packets destination
     * @return DatagramPacket
     *
     * Constructs a new message of the form
     * <From IP:PORT>: message from MESSAGE packet
     * The keywordID will always be INCOMMING
     *
     * If the new message is bigger than 256, this will truncate the message,
     * gauranteeing there won't be a IndexOutOfBounds exception
     */
    public static DatagramPacket
	createIncommingPacket(String message,
			      InetAddress origAddress,
			      int origPort,
			      InetAddress destAddress,
			      int destPort) {
	byte[] buf = new byte[PACKETSIZE];
	byte keywordID; //This will always be INCOMMING (3);
	String newMessage; //Will be of the form "<From host:port>: message"
	byte[] newMessageBytes;

	keywordID = INCOMMING;
	newMessage =  ("<From " + origAddress.getHostAddress() + ":" +
		       origPort +">: ");
	newMessage += message;
	newMessageBytes = newMessage.getBytes();
	buf[0] = keywordID;
	//Copy the contens of newMessageBytes into buf, 
	// starting at buf[1]:
	System.arraycopy(newMessageBytes, 0,
			 buf, KEYWORDLENGTH, MESSAGELENGTH);

	return new DatagramPacket(buf, buf.length, destAddress, destPort);

    } // end createIncommingPacket

    /**
     * @param destAddress - InetAddress of the packet's destination
     * @param port - int port of the packet's destination
     * @return DatagramPacket
     *
     * Simply returns a GREETING packet by setting the first byte of the 
     * UDP packet's data to GREETING (1)
     */
    public static DatagramPacket
	createGreetingPacket(InetAddress destAddress,
			     int port) {
	byte[] buf = new byte[PACKETSIZE];
	byte   keywordID = GREETING;

	buf[0] = keywordID;

	return new DatagramPacket(buf, buf.length, destAddress, port);
    } // end createGreetingPacket

    /**
     * @param line - String of command that the user input
     * @param destAddress - InetAddress of the packet's destination
     * @param port        - int port of the packet's destination
     * @throws Exception  - thrown if the message is too big
     *                      or the command is invalid
     * @return DatagramPacket
     *
     * First creates a space-delimited array of the command (words)
     * The first element is the keyword (which will map to a
     * keywordID byte), and the remaining elements constitue
     * the message.  
     * message's raw bytes will be copied to the packet's
     * Data field without encryption
     */
    public static DatagramPacket
	createMessagePacket(String line,
			    InetAddress destAddress,
			    int port) throws Exception {
	byte[] buf = new byte[PACKETSIZE];
	String[] words; 

	String message; //Everything after the initial keyword
	byte[] messageBytes;
	String keyword; //The initial word in line (ie - MESSAGE, etc.)
	byte   keywordID; //Encode the message string to a single byte 

	words = line.split(" "); //Split the input line between spaces

	try {
	    keyword = words[0];
	} catch (ArrayIndexOutOfBoundsException e) {
	    keyword = "";
	}

	message = "";

	//Piece together the message, based on the last
	// (length-1) elements of message
	for (int i=1; i<words.length; i++) {
	    message += words[i];
	    message += " ";
	}
	
	if (keyword.equals("MESSAGE"))
	    keywordID = MESSAGE;
	else
	    throw new Exception("INVALIDE KEYWORD: \"" + keyword +
				"\". Valid choices: MESSAGE");

	messageBytes = message.getBytes();

	//The format for buf will be "keywordID:messageBytes"
	buf[0] = keywordID;

	//Copy messageBytes into buf starting at buf[1]
	try {
	    System.arraycopy(messageBytes, 0,
			     buf, KEYWORDLENGTH, messageBytes.length);
	} catch (ArrayIndexOutOfBoundsException e) {
	    throw new Exception("BAD MESSAGE: message is too large");
	}

	return new DatagramPacket(buf, buf.length, destAddress, port);

    } //end createMessagePacket

    /** 
     * @param packet - DatagramPacket
     * @return String
     *
     * This simply extracts the message from the packet
     */
    public static String extractMessage(DatagramPacket packet) {
	return new String(packet.getData(), KEYWORDLENGTH, 
			  packet.getLength() - KEYWORDLENGTH);
    }

    /** 
     * @param packet - DatagramPacket
     * @return String
     *
     * This simply extracts the keywordID from the packet
     */
    public static byte extractKeywordID(DatagramPacket packet) {
	//Return the first byte of packet
	return packet.getData()[0];
    }

} //end ChatProtocol
